home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / interapplication comm / finderlaunch / testfinderlaunch.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  17.6 KB  |  566 lines

  1. /*    File:        TestFinderLaunch.c
  2.     
  3.     Description: 
  4.              A test application for sending an open documents Apple event to the
  5.             Finder.  This application calls the FinderLaunch routine defined
  6.             in FinderLaunch.c.
  7.             
  8.             This file is organized into the following sections:
  9.  
  10.             HFS OBJECT SELECTION
  11.                 routines calling Navigation Services or Standard file allowing
  12.                 the user to select either a file or a directory.
  13.             CALLING FinderLaunch
  14.                 a routine that calls the FinderLaunch routine defined in the file
  15.                 FinderLaunch.c.  Here, after gathering a list of files/folders
  16.                 from the user, an open documents apple event is sent to the
  17.                 Finder specifying the items selected.  The Finder, in turn,
  18.                 with launch/display/open the items as appropriate.
  19.             MAIN WINDOW
  20.                 routines for drawing and handling clicks in the main window.
  21.             MENU HANDLING
  22.                 menu handleing code
  23.             APPLE EVENT HANDLERS
  24.                 apple event handlers for open and quit application events.
  25.             EVENT HANDLING
  26.                 event dispatching code.  calls to WaitNextEvent are made here.
  27.             MAIN PROGRAM
  28.                 the main program routine including initialization code, the main
  29.                 loop, and teardown code.
  30.  
  31.     Author:    John Montbriand
  32.  
  33.     Copyright: 
  34.             Copyright © 1999 by Apple Computer, Inc.
  35.             All rights reserved worldwide.
  36.     
  37.     Disclaimer:
  38.             You may incorporate this sample code into your applications without
  39.             restriction, though the sample code has been provided "AS IS" and the
  40.             responsibility for its operation is 100% yours.  However, what you are
  41.             not permitted to do is to redistribute the source as "DSC Sample Code"
  42.             after having made changes. If you're going to re-distribute the source,
  43.             we require that you make it clear in the source that the code was
  44.             descended from Apple Sample Code, but that you've made changes.
  45.     
  46.     Change History (most recent first):
  47.             9/13/99 created by John Montbriand
  48. */
  49.  
  50. #include "TestFinderLaunch.h"
  51. #include "FinderLaunch.h"
  52.  
  53. #include <QuickDraw.h>
  54. #include <Menus.h>
  55. #include <Windows.h>
  56. #include <Dialogs.h>
  57. #include <Fonts.h>
  58. #include <SegLoad.h>
  59. #include <Resources.h>
  60. #include <Balloons.h>
  61. #include <Devices.h>
  62. #include <AppleEvents.h>
  63. #include <StdIO.h>
  64. #include <TextUtils.h>
  65. #include <string.h>
  66. #include <Gestalt.h>
  67. #include <Appearance.h>
  68.  
  69. #include <StandardFile.h>
  70. #include <Navigation.h>
  71.  
  72.  
  73.     /* true while the app is running */
  74. Boolean gRunning = true;
  75.  
  76. #ifndef __MWERKS__
  77. QDGlobals    qd;    /* QuickDraw globals*/
  78. #endif
  79.  
  80. FileFilterYDUPP gFileFilter;
  81. DlgHookYDUPP gSFHook;
  82. NavEventUPP gNavEventProc;
  83. AEIdleUPP gAEIdleProc = NULL; /* idle proc called from AEInteractWithUser */
  84. DialogPtr gMainDialog = NULL;
  85.  
  86. Boolean gForground = true;
  87. Boolean gAppleEvents = false;
  88. Boolean gAppearance = false;
  89.  
  90.  
  91.  
  92. /* HFS OBJECT SELECTION ------------------------------------------------ */
  93.  
  94.     /* in this section we define a set of routines for selecting a file or a folder.
  95.     Here, NavigationServices is used when it is present, otherwise we fall
  96.     back to useing the standard file alerts for system 7.  Note that Navigation
  97.     services is strictly PowerPC only, so any parts referring to that API are
  98.     bracketed in conditional statements.
  99.     
  100.     These routines are here so there is a way to choose a file or folder from the
  101.     main application.  After selecting a file or folder, an Apple event is sent to
  102.     the finder asking it to open/launch/display the item. */
  103.     
  104.  
  105. /* InvisoFilter is a file filter procedure passed to the CustomGetFile routine.  Its
  106.     purpose is to prevent the display of invisible files and folders in the
  107.     standard file window. */
  108. static pascal Boolean InvisoFilter(ParmBlkPtr PB, void *yourDataPtr) {
  109.     CInfoPBRec *cat = (CInfoPBRec *) PB;
  110.         /* filter invisible files and folders */
  111.     if ((cat->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) == 0)
  112.         return false;
  113.     else return true;
  114. }
  115.  
  116. /* MySFHook is a dialog hook routine passed to the CustomGetFile routine.  Its purpose
  117.     is to maintain the 'Select' button in the bottom right corner of the window.  This
  118.     button allows users to select a directory rather than navigate into it.  If the
  119.     user clicks on the 'Select' button while a folder is hilited in the list view, then
  120.     the folder is passed back to CustomGetFile's caller. */
  121. static pascal short MySFHook(short item, DialogPtr dialog, void *yourDataPtr) {
  122.     
  123.     static Boolean gFolderSelected = false;
  124.     static ControlHandle gFolderControl = NULL;
  125.     StandardFileReply *theReply;
  126.     
  127.     if (GetWRefCon(dialog) != sfMainDialogRefCon) return item;
  128.     theReply = (StandardFileReply *) yourDataPtr;
  129.     if (item == sfHookFirstCall) {
  130.         short itemt;
  131.         Rect itemb;
  132.         GetDialogItem(dialog, kMySFSelectButton, &itemt, (Handle*) &gFolderControl, &itemb);
  133.         HiliteControl(gFolderControl, 255);
  134.         gFolderSelected = false;
  135.     } else if ((item == kMySFSelectButton) && gFolderSelected) {
  136.         return sfItemOpenButton;
  137.     } else {
  138.         if (theReply->sfIsFolder || theReply->sfIsVolume) {
  139.             if ( ! gFolderSelected) {
  140.                 HiliteControl(gFolderControl, 0);
  141.                 gFolderSelected = true;
  142.             }
  143.         } else if (gFolderSelected) {
  144.             HiliteControl(gFolderControl, 255);
  145.             gFolderSelected = false;
  146.         }
  147.     }
  148.     return item;
  149. }
  150.  
  151.  
  152. #if TARGET_CPU_PPC
  153.  
  154. /* NavEventCallBack is a callback routine provided to the NavChooseObject routine.  In
  155.     this routine we process update and activate events for the main window while
  156.     navigation services is displaying its window. */ 
  157. static pascal void NavEventCallBack( NavEventCallbackMessage callBackSelector,
  158.             NavCBRecPtr callBackParms, NavCallBackUserData callBackUD) {
  159.     if (callBackSelector == kNavCBEvent) {
  160.         short ewhat;
  161.         ewhat = callBackParms->eventData.eventDataParms.event->what;
  162.         if ((ewhat == updateEvt) || (ewhat == activateEvt)) {
  163.         
  164.             HandleNextEvent(callBackParms->eventData.eventDataParms.event);
  165.  
  166.         }
  167.     }
  168. }
  169.  
  170. #endif
  171.  
  172.  
  173. /* GetHFSObjectList opens a communication session with the user allowing them to choose
  174.     one more file system objects.  After the user has made a selection, a list of the items
  175.     chosen is passed back as a AEDescList containing a list of FSSpec records. if the user
  176.     cancels the interaction, then no list is returned and a userCanceledErr is returned.*/
  177. static OSErr GetHFSObjectList(AEDescList *documents) {
  178. #if TARGET_CPU_PPC
  179.     NavReplyRecord theReply;
  180. #endif
  181.     Boolean hasNavReply;
  182.     OSErr err;
  183.         /* set up locals */
  184.     AECreateDesc(typeNull, NULL, 0, documents);
  185.     hasNavReply = false;
  186.     
  187. #if TARGET_CPU_PPC
  188.     if (NavServicesAvailable()) {
  189.         NavDialogOptions dialogOptions;
  190.             /* set the message in the navigation window to indicated multiple
  191.             selections are allowed */
  192.         memset(&theReply, 0, sizeof(theReply));
  193.         err = NavGetDefaultDialogOptions(&dialogOptions);
  194.         if (err != noErr) goto bail;
  195.         dialogOptions.dialogOptionFlags = (kNavDontAutoTranslate | kNavAllowMultipleFiles);
  196.         GetIndString(dialogOptions.message, kMainStrings, kNavTextMessage);
  197.             /* run the navigation window */
  198.         err = NavChooseObject( NULL, &theReply, &dialogOptions, gNavEventProc, NULL, NULL);
  199.         if (err != noErr) goto bail;
  200.         if (!theReply.validRecord) { err = userCanceledErr; goto bail; }
  201.         hasNavReply = true;
  202.             /* duplicate the returned document list */
  203.         err = AEDuplicateDesc(&theReply.selection, documents);
  204.         if (err != noErr) goto bail;
  205.             /* clean up the navigation stuff */
  206.         NavDisposeReply(&theReply);
  207.     } else
  208. #endif
  209.     {    Point where = {100, 100};
  210.         SFTypeList typeList;
  211.         StandardFileReply reply;
  212.             /* set up locals
  213.         SetPt(&where, 100, 100);
  214.             /* run the standard file window */
  215.         CustomGetFile(gFileFilter, -1, typeList, &reply, 130, where, gSFHook, NULL, NULL, NULL, &reply);
  216.         if (!reply.sfGood) { err = userCanceledErr; goto bail; }
  217.             /* if successful, save the selection to a list descriptor */
  218.         err = AECreateList(NULL, 0, false, documents);
  219.         if (err != noErr) goto bail;
  220.         err = AEPutPtr(documents, 0, typeFSS, &reply.sfFile, sizeof(FSSpec));
  221.         if (err != noErr) goto bail;
  222.     }
  223.     return noErr;
  224.     
  225.         /* error recovery */
  226. bail:
  227. #if TARGET_CPU_PPC
  228.     if (hasNavReply) NavDisposeReply(&theReply);
  229. #endif
  230.     AEDisposeDesc(documents);
  231.     return err;
  232. }
  233.  
  234.  
  235.  
  236. /* CALLING FinderLaunch ------------------------------------------------ */
  237.  
  238.     /* in this section, we call through to the FinderLaunch() routine defined in the
  239.     file FinderLaunch.c.  After calling GetHFSObjectList to retrieve a list of one or
  240.     more FSSpec records referring to a number of hfs objects, we coerce this list
  241.     into an array of FSSpec records and pass it to the FinderLaunch routine. */
  242.     
  243.     
  244. /* SelectTargetsToLaunch calls GetHFSObjectList to retrieve a list of files or folders    
  245.      and then it passes the selected folders and files to the FinderLaunch routine. */
  246. static void SelectTargetsToLaunch(void) {
  247.     AEDescList documents;
  248.     OSErr err;
  249.     long index, count;
  250.     FSSpec *targets;
  251.     AEKeyword keyword;
  252.     DescType typecode;
  253.     Size actualSize;
  254.         /* set up locals */
  255.     AECreateDesc(typeNull, NULL, 0, &documents);
  256.     targets = NULL;
  257.         /* get a list of files from the user */
  258.     err = GetHFSObjectList(&documents);
  259.     if (err != noErr) goto bail;
  260.         /* count the items in the list */
  261.     err = AECountItems(&documents, &count);
  262.     if (err != noErr) goto bail;
  263.         /* allocate an array to store the records */
  264.     targets = (FSSpec *) NewPtr(count * sizeof(FSSpec));
  265.     if (targets == NULL) { err = memFullErr; goto bail; }
  266.         /* copy each record from the list to the array */
  267.     for (index = 0; index < count; index++) {
  268.         err = AEGetNthPtr(&documents, (index + 1), typeFSS, &keyword, &typecode,
  269.             (targets + index), sizeof(FSSpec), &actualSize);
  270.         if (err != noErr) goto bail;
  271.     }
  272.         /* ask the Finder to launch the items */
  273.     err = FinderLaunch(count, targets);
  274.     
  275.         /* clean up and leave, report any 'real' errors */
  276. bail:
  277.     if ((err != noErr) && (err != userCanceledErr)) {
  278.         Str255 errStr;
  279.         NumToString(err, errStr);
  280.         ParamAlert(kSelectAbortedError, errStr, NULL);
  281.     }
  282.     if (targets != NULL) DisposePtr((Ptr) targets);
  283.     AEDisposeDesc(&documents);
  284. }
  285.  
  286.  
  287.  
  288.  
  289.  
  290. /* MAIN WINDOW ------------------------------------------------ */
  291.  
  292.  
  293. /* HitMainWindow is called when DialogSelect returns true for the main
  294.     dialog window.  This usually indicates that there has been a mouse
  295.     down inside of the main window. */
  296. static void HitMainWindow(DialogPtr theDialog, EventRecord *ev, short itemNo) {
  297.     if (itemNo == kMainSelectButton)
  298.         SelectTargetsToLaunch();
  299. }
  300.  
  301.  
  302. /* RedrawMainDialogWindow redraws the main window honoring the current
  303.     activation state of the window. */
  304. static void RedrawMainDialogWindow(DialogPtr theDialog) {
  305.     if (theDialog != NULL) {
  306.         short itemt;
  307.         ControlHandle theControl;
  308.         Rect itemb;
  309.         
  310.         GetDialogItem(theDialog, kMainSelectButton, &itemt, (Handle*) &theControl, &itemb);
  311.         if ((theDialog == FrontWindow()) && gForground)
  312.             HiliteControl(theControl, 0);
  313.         else HiliteControl(theControl, 255);
  314.         DrawDialog(theDialog);
  315.     }
  316. }
  317.  
  318.  
  319.  
  320. /* MENU HANDLING ------------------------------------------------ */
  321.  
  322.  
  323. /* ResetMenus is called to reset the menus immediately before
  324.     either MenuSelect or MenuKey is called.  Here, we disable the
  325.     quit command during file copies. */
  326. static void ResetMenus(void) {
  327.     /* nothing to do here */
  328. }
  329.  
  330.  
  331. /* DoMenuCommand is called after either MenuSelect of MenuKey.  The
  332.     parameter rawMenuSelectResult is the result from one of these two routines. 
  333.     DoMenuCommand parses this result into its two parts, and dispatches
  334.     the menu command as appropriate. */
  335. static void DoMenuCommand(long rawMenuSelectResult) {
  336.     short menu, item;
  337.         /* decode the MenuSelect result */
  338.     menu = (rawMenuSelectResult >> 16);
  339.     if (menu == 0) return;
  340.     item = (rawMenuSelectResult & 0x0000FFFF);
  341.         /* dispatch on result */
  342.     switch (menu) {
  343.         case mApple:
  344.             if (item == iAbout) {
  345.                     /* show the about box. */
  346.                 ParamAlert(kAboutBoxError, NULL, NULL);
  347.             } else if (item >= iFirstAppleItem) {
  348.                 Str255 deskAccName;
  349.                     /* open an apple menu item. */
  350.                 GetMenuItemText(GetMenuHandle(mApple), item, deskAccName);
  351.                 OpenDeskAcc(deskAccName);
  352.             }
  353.             break;
  354.         case mFile:
  355.             if (item == iSelectTargets)
  356.                 SelectTargetsToLaunch();
  357.             else if (item == iQuit) 
  358.                 gRunning = false;
  359.             break;
  360.     }
  361.         /* unhilite the menu once we're done the command */
  362.     HiliteMenu(0);
  363. }
  364.  
  365.  
  366.  
  367. /* APPLE EVENT HANDLERS ------------------------------------------------ */
  368.  
  369. /* OpenApplication is an apple event handler called for 'open application' apple events. */
  370. static pascal OSErr OpenApplication(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) {
  371.     gMainDialog = GetNewDialog(kMainDialog, NULL, (WindowPtr) (-1));
  372.     return noErr;
  373. }
  374.  
  375. /* CloseApplication is an apple event handler called for 'close application' apple events. */
  376. static pascal OSErr CloseApplication(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) {
  377.     gRunning = false;
  378.     return noErr;
  379. }
  380.  
  381.  
  382.  
  383. /* EVENT HANDLING ------------------------------------------------ */
  384.  
  385.  
  386. /* HandleNextEvent handles the event in the event record *ev dispatching
  387.     the event to appropriate routines.   */
  388. void HandleNextEvent(EventRecord *ev) {
  389.     DialogPtr theDialog;
  390.     WindowPtr theWindow;
  391.     short itemNo;
  392.     
  393.         /* dialog pre-processing */
  394.     if (((ev->what == keyDown) || (ev->what == autoKey)) && ((ev->modifiers & cmdKey) != 0)) {
  395.         ResetMenus();
  396.         DoMenuCommand(MenuKey((char) (ev->message & charCodeMask)));
  397.     } else if (ev->what == osEvt) {
  398.             /* process manager switches */
  399.         if ( (((ev->message >> 24) & 0x0FF) == suspendResumeMessage) && ((ev->message & resumeFlag) != 0)) {
  400.             gForground = true;
  401.         } else gForground = false;
  402.         RedrawMainDialogWindow(gMainDialog);
  403.     } else if (ev->what == activateEvt) {
  404.         if ((gMainDialog == ((DialogPtr) ev->message)))
  405.             RedrawMainDialogWindow(gMainDialog);
  406.     }
  407.  
  408.         /* handle clicks in the dialog window */
  409.     if (IsDialogEvent(ev))
  410.         if (DialogSelect(ev, &theDialog, &itemNo)) {
  411.             if (theDialog == gMainDialog)
  412.                 HitMainWindow(theDialog, ev, itemNo);
  413.         }
  414.  
  415.         /* clicks and apple events... */
  416.     if (ev->what == kHighLevelEvent) {
  417.         AEProcessAppleEvent(ev);
  418.     } else if (ev->what == mouseDown)
  419.         switch (FindWindow(ev->where, &theWindow)) {
  420.             
  421.                 /* menu bar clicks */
  422.             case inMenuBar:
  423.                 ResetMenus();
  424.                 DoMenuCommand(MenuSelect(ev->where));
  425.                 break;
  426.                 
  427.                 /* clicks in the close box, close the app */
  428.             case inGoAway:
  429.                 if (TrackGoAway(theWindow, ev->where)) {
  430.                     gRunning = false;
  431.                 }
  432.                 break;
  433.                 
  434.                 /* allow window drags */
  435.             case inDrag:
  436.                 if (theWindow == FrontWindow()) {
  437.                     Rect boundsRect = { -32000, -32000, 32000, 32000};
  438.                     DragWindow(theWindow, ev->where, &boundsRect);
  439.                 }
  440.                 break;
  441.                 
  442.                 /* desktop clicks, etc... */
  443.             case inSysWindow:
  444.                 SystemClick(ev, theWindow);
  445.                 break;
  446.         }
  447. }
  448.  
  449.  
  450. /* ProcessNextEvent calls WaitNextEvent to get the next event and then it passes
  451.     the event along to the HandleNextEvent routine.  sleepTime is passed to the
  452.     WaitNextEvent routine in the sleep parameter. */
  453. void ProcessNextEvent(long sleepTime) {
  454.     EventRecord ev;
  455.         /* get the next event */
  456.     if ( ! WaitNextEvent(everyEvent, &ev,  sleepTime, NULL) )
  457.         ev.what = nullEvent;
  458.     HandleNextEvent(&ev);
  459. }
  460.  
  461. /* FDPIdleProcedure is the idle procedure called by AEInteractWithUser while we are waiting
  462.     for the application to be pulled into the forground.  It simply passes the event along
  463.     to HandleNextEvent */
  464. static pascal Boolean FDPIdleProcedure(EventRecord *theEvent, long *sleepTime, RgnHandle *mouseRgn) {
  465.     HandleNextEvent(theEvent);
  466.     return false;
  467. }
  468.  
  469.  
  470. /* ParamAlert is a general alert handling routine.  If Apple events exist, then it
  471.     calls AEInteractWithUser to ensure the application is in the forground, and then
  472.     it displays an alert after passing the s1 and s2 parameters to ParamText. */
  473. short ParamAlert(short alertID, StringPtr s1, StringPtr s2) {
  474.     AEInteractWithUser(kNoTimeOut, NULL, gAEIdleProc);
  475.     ParamText(s1, s2, NULL, NULL);
  476.     return Alert(alertID, NULL);
  477. }
  478.  
  479.  
  480.  
  481. /* MAIN PROGRAM ------------------------------------------------ */
  482.  
  483. int main(void) {
  484.     OSErr err;
  485.     AEEventHandlerUPP aehandler;
  486.     long response;
  487.     
  488.         /* set up our app */
  489.     SetApplLimit(GetApplLimit());
  490.     MaxApplZone();
  491.     InitGraf(&qd.thePort);
  492.     InitFonts();
  493.     InitWindows();
  494.     TEInit();
  495.     InitMenus();
  496.     InitDialogs(0);
  497.     FlushEvents(everyEvent, 0);
  498.     InitCursor();
  499.  
  500.  
  501.         /* apple events??? */
  502.     if (Gestalt(gestaltAppleEventsAttr, &response) != noErr) response = 0;
  503.     gAppleEvents = ((response & (1<<gestaltAppleEventsPresent)) != 0);
  504.     if ( ! gAppleEvents) {
  505.         ParamAlert(kReqMgrsNotAvailError, NULL, NULL);
  506.         err = userCanceledErr;
  507.         goto bail;
  508.     }
  509.         /* appearance */
  510.     if (Gestalt(gestaltAppearanceAttr, &response) != noErr) response = 0;
  511.     if ((response & (1<<gestaltAppearanceExists)) != 0) {
  512.         err = RegisterAppearanceClient();
  513.         if (err != noErr) goto bail;
  514.         gAppearance = true;
  515.     }
  516.  
  517. #if TARGET_CPU_PPC
  518.     if (NavServicesAvailable())
  519.         NavLoad();
  520.     gNavEventProc = NewNavEventProc(NavEventCallBack);
  521.     if (gNavEventProc == NULL) { err = memFullErr; goto bail; }
  522. #endif
  523.     gFileFilter = NewFileFilterYDProc(InvisoFilter);
  524.     if (gFileFilter == NULL) { err = memFullErr; goto bail; }
  525.     gSFHook = NewDlgHookYDProc(MySFHook);
  526.     if (gSFHook == NULL) { err = memFullErr; goto bail; }
  527.     gAEIdleProc = NewAEIdleProc(FDPIdleProcedure);
  528.     if (gAEIdleProc == NULL) { err = memFullErr; goto bail; }
  529.     
  530.         /* standard apple events */
  531.     aehandler = NewAEEventHandlerProc(OpenApplication);
  532.     if (aehandler == NULL) { err = memFullErr; goto bail; }
  533.     err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, aehandler, 0, false);
  534.     if (err != noErr) goto bail;
  535.     aehandler = NewAEEventHandlerProc(CloseApplication);
  536.     if (aehandler == NULL) { err = memFullErr; goto bail; }
  537.     err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, aehandler, 0, false);
  538.     if (err != noErr) goto bail;
  539.  
  540.         /* set up the menu bar */
  541.     SetMenuBar(GetNewMBar(128));
  542.     DrawMenuBar();
  543.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');
  544.  
  545.         /* run the app */
  546.     while (gRunning) {
  547.         
  548.         ProcessNextEvent(-1);
  549.         
  550.     }
  551.     
  552. bail:
  553. #if TARGET_CPU_PPC
  554.     if (NavServicesAvailable())
  555.         NavUnload();
  556. #endif
  557.     if (err != noErr && err != userCanceledErr) {
  558.         Str255 errStr;
  559.         NumToString(err, errStr);
  560.         ParamAlert(kProgramAbortedError, errStr, NULL);
  561.     }
  562.     if (gAppearance)
  563.         UnregisterAppearanceClient();
  564.     ExitToShell();
  565.     return 0;
  566. }